package inl.resilienceManager.qmul.gemom;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import resilienceManager.qmul.gemom.AuctionFrame;
import resilienceManager.qmul.gemom.Datasaver;
import resilienceManager.qmul.gemom.Gbroker;
import resilienceManager.qmul.gemom.NSitem;
import resilienceManager.qmul.gemom.RectObject;
import resilienceManager.qmul.gemom.Renderer;
import resilienceManager.qmul.gemom.XMLfilter;
import resilienceManager.qmul.gemom.nsParser;
import util.rm.quml.gemom.CovMatrixData;
import cbr.resilienceManager.qmul.gemom.CbrData;

/** COPYRIGHT DISCLAIMER
 * Synopsis: All the source codes presented in this document are intellectual
 * properties of Queen Mary, University of London
 * @year 2010
 * @author Peng Jiang, John Bigham, Jiayi Wu Jinfu Wang------------QMUL
 * @version 1.0 
 * */


public class treePanel extends JPanel{
	
	JTextField textField = new JTextField();

	JScrollPane scrollPane = new JScrollPane();
	
	JButton auctionButton;

	static JList jlist;

	static JTree tree;
	
	private JPanel self; 

	Renderer renderer = new Renderer();

	File file;
// namespace
//	XXX model Itemmodel
	DefaultTreeModel[] Itemmodel= new DefaultTreeModel[100];

	int Item_num=0;
	
// all items?
	NSitem[] items;
	
	// used for the case based reasoning
	public CbrData cd;
	
	
	public Gbroker[] gblist;
	
	
	public iniFrame up;
	
	
	public solution[] sl;
	
	private boolean itemDivided = false;
	
	
	/**
	 * @param glist: the list of the gbroker
	 * */
	
	public void setGlist(Gbroker[] glist)
	{
		gblist=glist;
	}



	public treePanel(iniFrame f, CbrData cbrDa) {
		
		self = this;
		up=f;
		//setup the CBR
		this.cd=cbrDa;
//		gblist=up.getGlist();
		DefaultMutableTreeNode top = new DefaultMutableTreeNode("root");
		tree = new JTree(top);

		tree.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent event) {
				if (((event.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
						&& (tree.getSelectionCount() > 0)) {
					showMenu(event.getX(), event.getY());
				}
			}
		});


		setLayout(new BorderLayout());
		tree.setCellRenderer(renderer);
		tree.addTreeSelectionListener(new TreeHandler());
		scrollPane.getViewport().add(tree);
		add("Center", scrollPane);
		add("North", textField);

		// set up the button panel
		JPanel panel = new JPanel(new GridLayout(1, 3));

		JButton divButton = new JButton("Open");
		divButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				//divideSelectedItems(true,null);

				System.out.println("divButton Open");
				setupTree();
				
				System.out.println("divButton Open - setupTree");
//				TODO createVTable when saving

				// disable optimization button as the auction function can be 
				// only used when the user has properly divided items.
				auctionButton.setEnabled(false);
//				up.createVTable();
				//new namespaces have been loaded and should be allowed to divided
				itemDivided = false;
			}
		});


//FIXME initiate the search for solutions, *add utility function*
// TODO each item and its ns with a penalty (all 1) and revenue value (all 0)
//		TODO another search for mirrors
		
		
		

		
		
		final JButton starButton = new JButton("Mirror");
		starButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				
				gblist=up.getGlist();
				handleMirror(gblist, sl);
//				handleStar(items,gblist);
				
//				up.getSolution(sl, gblist);
				
//				up.getRisk(sl, gblist);
				
			}
		});
		
//		starButton.enable(false);
		starButton.setEnabled(false);
		
		auctionButton = new JButton("Allocation");
		auctionButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				
				gblist=up.getGlist();
				
				handleSolution(items,gblist);
//			TODO result GUI	
				up.getSolution(sl, gblist);
				
//				saveSolution(sl);
				
				up.getRisk(sl, gblist);
				  
//				up.getUtility(sl, gblist);
//				starButton.enable(true);
				starButton.setEnabled(true);
			}
		}); 
		
////		TODO use the bid search instead of a* or auction 
//		final JButton searchButton = new JButton("search");
//		auctionButton.addActionListener(new java.awt.event.ActionListener(){
//			public void actionPerformed(ActionEvent event) {
//				
//				gblist=up.getGlist();
//				
//				handleSolution(items,gblist);
//				
//				up.getSolution(sl, gblist);
//				
//				up.getRisk(sl, gblist);
//				
////				up.getUtility(sl, gblist);
//				
//			}
//		});
		
		auctionButton.setEnabled(false);
		
		
		JButton saveButton = new JButton("Save");
		saveButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				 
				if(iniFrame.brokerConfigured){
					// divide items, also check if items have been already divided.
					if(!itemDivided  ){
						saving();
						TableSaver.createBrokersTab();
						gblist=up.getGlist();
						TableSaver.saveBrokers(gblist);
						items=Datasaver.getItems(cd.getTableID());
										
						up.createPie(items);
						itemDivided = true;
					}
					else{
						JOptionPane.showMessageDialog(self, "Item sets have already been divided, please" +
								" load namespaces files to divide item sets again.",
								"Item already divided", JOptionPane.INFORMATION_MESSAGE);
								 
					}
				
				}
				else{
					JOptionPane.showMessageDialog(self, "You have an empty broker setting, please" +
						" configure broker setting first before setting namespaces.",
						"Broker configuration unavailable", JOptionPane.INFORMATION_MESSAGE);
						 
					
				}
				
				/*
				Gbroker[] gs= up.getGlist();
//				XXX other DB setup
				CovMatrixData.doDB(gs);
				up.createVTable();
				
				
//				XXX create the soltab 
				TableSaver.createSoltab(up.getGlist());				;
				auctionButton.setEnabled(true);
//				starButton.enable(true);
 
                */
			}
		});
		 
		panel.add(divButton);
		panel.add(saveButton);
		panel.add(auctionButton);
		panel.add(starButton);

		add("South", panel);

		// set up the list panel

		String labels[] = { "Root    " };

		jlist = new JList(labels);

		jlist.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);


		ListSelectionListener listselection = new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent listSelectionEvent) {

				boolean adjust = listSelectionEvent.getValueIsAdjusting();
				if (!adjust) {
					JList list = (JList) listSelectionEvent.getSource();
					int selections = list.getSelectedIndex();
					showTree(selections);


				}
			}
		};

		jlist.addListSelectionListener(listselection);

		JScrollPane scrollPane1 = new JScrollPane(jlist);

		add("West", scrollPane1);
		 
	}
	
	/**
	 * Method used to process covariance matrix from data, and show item and variance tables
	 * **/
	public boolean processData(double[][] rr){
		
		// process data, calculate covariance matrix of namespaces and items and
		// display them in tables
		if(itemDivided){
			 

			
			Gbroker[] gs= up.getGlist();
//			XXX other DB setup
			CovMatrixData.doDB(gs, rr, up.getNS());

			
//			XXX create the soltab 
			TableSaver.createSoltab(up.getGlist());				;
			this.auctionButton.setEnabled(true);
			up.createVTable();
			up.createItemTable(items);
//			starButton.enable(true);
			return true;
 
		}
		else{
			JOptionPane.showMessageDialog(self, "Namespace sets have not been divided yet. To proceed please divide all" +
					"namespaces into arbitrary number of item sets first.",
					"Item sets not divided", JOptionPane.INFORMATION_MESSAGE);
			
			return false;
					 
		}
	
	}
	
	/**
	 * bid tree handling solution
	 * @param t: the list of items, NSItem[]
	 * @param glist: the list of Gbroker, Gbroker[]
	 * **/
	private void handleSolution(NSitem[] t, Gbroker[] glist){
		
		AuctionTree bt= new AuctionTree(t,gblist,0);
		
		bt.buildTree();
		
		
		
		DefaultMutableTreeNode root=bt.SearchTree();
		
		DefaultMutableTreeNode currentnode=root;
		
		
//		solution?

		String[] solution= new String[glist.length];
		
		sl= new solution[glist.length];

		for(int i=0; i<glist.length; i++)
		{

			DefaultMutableTreeNode inode=(DefaultMutableTreeNode) currentnode.getChildAt(0);

			// save the allocation
			solution[i]=inode.toString();
			
			sl[i]= new solution(glist[i].getID(),solution[i], items);

			currentnode=inode;

		}
		
//		XXX save the solution[]
		TableSaver.saveSol(new Sols(glist, sl));
//		handleMirror(glist, sl);
	}
	
	private void handleMirror( Gbroker[] gglist, solution[] sol){
		
//		update broker
		updateBrokers(gglist, sol);
		
		for (int b = 0; b<gglist.length; b++){
			HashMap<Gbroker[], solution[]>  bsol= new HashMap<Gbroker[],solution[]>();
			ArrayList<Gbroker[]> bdeads =new ArrayList<Gbroker[]>();



			//		deadlist
			Gbroker[] dl = new Gbroker[]{gglist[b]};
			bdeads.add(dl);
			//		dead items to bid for
			ArrayList<NSitem> deadal= gglist[b].getCurrentItems();
			NSitem[] deaditems = deadal.toArray(new NSitem[deadal.size()]);
			
			//		list of bidding brokers

			Gbroker[] gl= popBrokerArray(gglist, b);
// TODO if dead items are null or size==0
			if(deaditems==null || deaditems.length==0){
				TableSaver.saveEmptySol(gglist, dl, gl);
				continue;
			}
			
			//	}
			//		auction tree
			AuctionTree bt= new AuctionTree(deaditems,gl,0);

			bt.buildTree();



			DefaultMutableTreeNode root=bt.SearchTree();

			DefaultMutableTreeNode currentnode=root;


			//		solution?

			String[] solution= new String[gl.length];

			//		mirror solution for one broker
			//		XXX need update? mirror for second search
			solution[] ms= new solution[gl.length];

			for(int i=0; i<gl.length; i++)
			{

				DefaultMutableTreeNode inode=(DefaultMutableTreeNode) currentnode.getChildAt(0);

				// save the allocation
				solution[i]=inode.toString();

				ms[i]= new solution(gl[i].getID(),solution[i], items);

				currentnode=inode;

			}

			//		bsol.put(dl, ms);
			//		for (Gbroker[] i:bdeads){
			//		 new Sols(gglist, dl, ms);
			//		}

			TableSaver.saveSol(new Sols(gglist, dl, ms));
		}
	}
	
	
	/**
	 * A star search handling solution
	 * @param t: the list of items, NSItem[]
	 * @param glist: the list of Gbroker, Gbroker[]
	 * **/
	
	private void handleStar(NSitem[] t, Gbroker[] glist){
			
			StarTree st= new StarTree(t,gblist,0);
			
			st.buildTree();
			
			
			
			DefaultMutableTreeNode root=st.SearchTree();
			
			DefaultMutableTreeNode currentnode=root;

			String[] solution= new String[glist.length];
			
			sl= new solution[glist.length];

			for(int i=0; i<glist.length; i++)
			{

				DefaultMutableTreeNode inode=(DefaultMutableTreeNode) currentnode.getChildAt(0);

				// save the allocation
				solution[i]=inode.toString();
				
				sl[i]= new solution(glist[i].getID(),solution[i], items);

				currentnode=inode;

			}
				
		}
	
	

	/**
	 * the jlist selection function
	 * 
	 * when you select the item , show the tree structure
	 * */
	protected void showTree(int treeid)
	{

		DefaultMutableTreeNode top =(DefaultMutableTreeNode)Itemmodel[treeid].getRoot();


		DefaultTreeModel tmodel=new DefaultTreeModel(top);

		tree.setModel(tmodel);

		expandAll(tree,new TreePath(top),true);


	}

	/**
	 * the pop menu for the division and source import 
	 * 
	 * */
	protected void showMenu(int x, int y) {
		JPopupMenu popup = new JPopupMenu();
		JMenuItem mi = new JMenuItem("Divide");
		JMenuItem si = new JMenuItem("Source");
		TreePath path = tree.getSelectionPath();
		Object node = path.getLastPathComponent();
		if (node == tree.getModel().getRoot()) {
			mi.setEnabled(false);
		}
		popup.add(mi);
		popup.add(si);
		mi.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				divideSelectedItems(true,null);
			}
		});

		si.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				setupTree();
			}
		});

		popup.show(tree, x, y);

	}
	public void divideSelectedItems(boolean selected, DefaultMutableTreeNode removenode) {
		DefaultMutableTreeNode node;
		DefaultTreeModel model = (DefaultTreeModel) (tree.getModel());
		if(selected)
		{
		TreePath path = tree.getSelectionPath();
		node = (DefaultMutableTreeNode) (path.getLastPathComponent());
		}else
		{
			node=removenode;
		}
		model.removeNodeFromParent(node);

		Itemmodel[0]=new DefaultTreeModel((TreeNode) model.getRoot());
		Item_num=Item_num+1;
		Itemmodel[Item_num]= new DefaultTreeModel(node);


		DefaultListModel listmodel= new DefaultListModel();
		String ni;

		for(int i=1; i<Item_num+2; i++)
		{
			ni=new String("Item"+i);
			listmodel.addElement(ni);

		}


		jlist.setModel(listmodel);

		this.repaint();

	}




	/**
	 * import the information of Gbroker and 
	 * @return 
	 * */	
	
	public void SetGB(Gbroker[] gb)
	{
		this.gblist=gb;
	}


	/**
	 * Import the name space from the data base
	 * build a tree node
	 * @return 
	 * */
	private boolean setupTree()
	{

		DefaultMutableTreeNode top;

		JFileChooser fc=new JFileChooser();

		XMLfilter filter = new XMLfilter();

		fc.setFileFilter(filter);

		int returnVal = fc.showOpenDialog(this);

		if (returnVal == JFileChooser.APPROVE_OPTION) {
			file = fc.getSelectedFile();

			nsParser nsp=new nsParser(file,cd);
			nsp.buildNS();


			top=nsp.getSAXTree();
			
			DefaultTreeModel tmodel=new DefaultTreeModel(top);

			tree.setModel(tmodel);

			//DefaultMutableTreeNode root=(DefaultMutableTreeNode) tree.getModel().getRoot(); 
			//root.removeAllChildren();
			//addAll(root,top);
			expandAll(tree,new TreePath(top),true);
			tree.getParent().repaint();

			Itemmodel[0]=new DefaultTreeModel((DefaultMutableTreeNode)top.clone());
			Item_num=0;

			return true;
		}
		else
		{
			return false;
		}


	}
	
	

	//save all the divide item to the NSitem list
	private void saving(){
		
//		XXX create item0 if not exists
		TableSaver.createItemTab(0);
		items=new NSitem[Item_num+1];

		for(int i=0; i<Item_num+1; i++)
		{
			DefaultTreeModel dt=Itemmodel[i];
			items[i]=new NSitem(i);

			DefaultMutableTreeNode root=(DefaultMutableTreeNode)dt.getRoot();
			// save the fisrt root name space 
			int nsid=Datasaver.findID(root.toString(),cd.getTableID());
//			nsid is the number findID from table
			items[i].addNS(nsid);
			
			// save the rest of the name space
			visitAll(items[i],root);			
		}
		
		Datasaver.saveItems(items,cd.getTableID());
	}

	private void visitAll(final NSitem nst, DefaultMutableTreeNode node)
	{
		if (node.getChildCount() >= 0) {
			for (Enumeration e=node.children(); e.hasMoreElements(); ) {
				DefaultMutableTreeNode n = (DefaultMutableTreeNode)e.nextElement();
				int nsid=Datasaver.findID(n.toString(),cd.getTableID());
				nst.addNS((Integer)nsid);
				visitAll(nst,n);
			}
		}
	}

	// auto divide the namespace tree
	
	private int level=2;
	private void auto(DefaultMutableTreeNode node,int le){
		
		while(node.getChildCount() >= 0)
		{
			for (Enumeration e=node.children(); e.hasMoreElements(); ) {
				DefaultMutableTreeNode n = (DefaultMutableTreeNode)e.nextElement();
				if(le==level)
				{
					divideSelectedItems(false,n);
				}else
				{
					le=le+1;
					auto(n,le);
				}
			}
		}
		
		

	}


	private void expandAll(JTree tree, TreePath parent, boolean expand) {
		// Traverse children
		DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent.getLastPathComponent();
		if (node.getChildCount() >= 0) {
			for (Enumeration e=node.children(); e.hasMoreElements(); ) {
				DefaultMutableTreeNode n = (DefaultMutableTreeNode)e.nextElement();
//				System.out.println(n);
//				if(n.toString().startsWith("meanRate")){
////					HashMap
//					System.out.println(n);	
//				}else{
//					TreePath path = parent.pathByAddingChild(n);
//					expandAll(tree, path, expand);
//				}
				TreePath path = parent.pathByAddingChild(n);
				expandAll(tree, path, expand);
			}
		}

		// Expansion or collapse must be done bottom-up
		if (expand) {
			tree.expandPath(parent);
		} else {
			tree.collapsePath(parent);
		}
	}

	private void addAll(final DefaultMutableTreeNode root, final DefaultMutableTreeNode anode)
	{
		if (anode.getChildCount() >= 0) {
			for(Enumeration e=anode.children(); e.hasMoreElements();){
				DefaultMutableTreeNode n = (DefaultMutableTreeNode)e.nextElement();
				//addAll(root,n);
				root.add(n);
			}
		}
		else{
			//root.add(anode);
		}
	}

	private class TreeHandler implements TreeSelectionListener {
		public void valueChanged(TreeSelectionEvent e) {
			TreePath path = e.getPath();
			//String text = path.getPathComponent(path.getPathCount() - 1).toString();
			String text=Datasaver.pathtransfer(path.toString());
			textField.setText(text);
		}
	}

	
//build new arrary removing the index broker  
	public static Gbroker[] popBrokerArray(Gbroker[] ga, int index){
		ArrayList<Gbroker> g = new ArrayList<Gbroker>(Arrays.asList(ga)); 
		g.remove(index);
		return  g.toArray(new Gbroker[g.size()]);
	}
	
//	update brokers' current items with solution[]
	public static void updateBroker(Gbroker b, solution s){
		if(s.getAlist()!=null && s.getAlist().length!=0)
			b.setCurrentItems(s.getAlist());	
	}
	
	public static void updateBrokers(Gbroker[] bs, solution[] ss){
		for (Gbroker b : bs){
			for (solution s : ss){
				if (s.getGID()== b.getID()){
					updateBroker(b,s);
				}
			}
		}
	}

}
